/*
 * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <inttypes.h>
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "driver/i2c.h"
#include "esp_system.h"
#include "esp_err.h"
#include "esp_log.h"
#include "esp_check.h"
#include "esp_lcd_panel_io.h"
#include "esp_lcd_touch.h"

#include "realme_watch_touch.h"

#define POINT_NUM_MAX       (1)

#define DATA_START_REG      (0x8000)
#define CHIP_ID_REG         (0xA7)

static const char *TAG = "watch_touch";

#define BYTESHIFT(VAR, IDX) (((VAR) >> ((IDX) * 8)) & 0xFF)

static esp_err_t read_data(esp_lcd_touch_handle_t tp);
static bool get_xy(esp_lcd_touch_handle_t tp, uint16_t *x, uint16_t *y, uint16_t *strength, uint8_t *point_num, uint8_t max_point_num);
static esp_err_t del(esp_lcd_touch_handle_t tp);

static esp_err_t i2c_read_bytes(esp_lcd_touch_handle_t tp, uint16_t reg, uint8_t *data, uint8_t len);
static esp_err_t i2c_write_bytes(esp_lcd_touch_handle_t tp, uint16_t reg, uint8_t *data, uint8_t len);

static esp_err_t reset(esp_lcd_touch_handle_t tp);
static esp_err_t touch_init(esp_lcd_touch_handle_t tp);

esp_err_t esp_lcd_touch_new_i2c_realme(const esp_lcd_panel_io_handle_t io, const esp_lcd_touch_config_t *config, esp_lcd_touch_handle_t *tp)
{
    ESP_RETURN_ON_FALSE(io, ESP_ERR_INVALID_ARG, TAG, "Invalid io");
    ESP_RETURN_ON_FALSE(config, ESP_ERR_INVALID_ARG, TAG, "Invalid config");
    ESP_RETURN_ON_FALSE(tp, ESP_ERR_INVALID_ARG, TAG, "Invalid touch handle");

    /* Prepare main structure */
    esp_err_t ret = ESP_OK;
    esp_lcd_touch_handle_t realme = calloc(1, sizeof(esp_lcd_touch_t));
    ESP_GOTO_ON_FALSE(realme, ESP_ERR_NO_MEM, err, TAG, "Touch handle malloc failed");

    /* Communication interface */
    realme->io = io;
    /* Only supported callbacks are set */
    realme->read_data = read_data;
    realme->get_xy = get_xy;
    realme->del = del;
    /* Mutex */
    realme->data.lock.owner = portMUX_FREE_VAL;
    /* Save config */
    memcpy(&realme->config, config, sizeof(esp_lcd_touch_config_t));

    /* Prepare pin for touch interrupt */
    if (realme->config.int_gpio_num != GPIO_NUM_NC) {
        const gpio_config_t int_gpio_config = {
            .mode = GPIO_MODE_INPUT,
            // 不使用中断
            // .intr_type = (realme->config.levels.interrupt ? GPIO_INTR_POSEDGE : GPIO_INTR_NEGEDGE),
            .pin_bit_mask = BIT64(realme->config.int_gpio_num)
        };
        ESP_GOTO_ON_ERROR(gpio_config(&int_gpio_config), err, TAG, "GPIO intr config failed");

        /* Register interrupt callback */
        if (realme->config.interrupt_callback) {
            esp_lcd_touch_register_interrupt_callback(realme, realme->config.interrupt_callback);
        }
    }
    /* Prepare pin for touch controller reset */
    if (realme->config.rst_gpio_num != GPIO_NUM_NC) {
        const gpio_config_t rst_gpio_config = {
            .mode = GPIO_MODE_OUTPUT,
            .pin_bit_mask = BIT64(realme->config.rst_gpio_num)
        };
        ESP_GOTO_ON_ERROR(gpio_config(&rst_gpio_config), err, TAG, "GPIO reset config failed");
    }
    /* Reset controller */
    ESP_GOTO_ON_ERROR(reset(realme), err, TAG, "Reset failed");
    /* Read product id */
    ESP_GOTO_ON_ERROR(touch_init(realme), err, TAG, "init failed");
    *tp = realme;

    return ESP_OK;
err:
    if (realme) {
        del(realme);
    }
    ESP_LOGE(TAG, "Initialization failed!");
    return ret;
}

static esp_err_t read_data(esp_lcd_touch_handle_t tp)
{
    uint8_t touch[8];
    uint8_t sen_data = 0;

    if(tp->config.int_gpio_num != GPIO_NUM_NC)
    {
        if(gpio_get_level(tp->config.int_gpio_num) == tp->config.levels.interrupt)
        {
            ESP_RETURN_ON_ERROR(i2c_read_bytes(tp, DATA_START_REG, touch, sizeof(touch)), TAG, "I2C read failed");
            ESP_RETURN_ON_ERROR(i2c_write_bytes(tp, 0x03, &sen_data, 1), TAG, "I2C write failed");

            portENTER_CRITICAL(&tp->data.lock);
            tp->data.points = 1;
            /* Fill all coordinates */
            tp->data.coords[0].x = (((uint16_t)touch[5] & 0x0f) << 8) | touch[4];
            tp->data.coords[0].y = (((uint16_t)touch[7] & 0x0f) << 8) | touch[6];
            portEXIT_CRITICAL(&tp->data.lock);
            // ESP_LOGI(TAG, "pos:===========X:%d==========Y:%d", tp->data.coords[0].x, tp->data.coords[0].y);
        }
    }
    return ESP_OK;
}

static bool get_xy(esp_lcd_touch_handle_t tp, uint16_t *x, uint16_t *y, uint16_t *strength, uint8_t *point_num, uint8_t max_point_num)
{
    assert(tp != NULL);
    assert(x != NULL);
    assert(y != NULL);
    assert(point_num != NULL);
    assert(max_point_num > 0);

    portENTER_CRITICAL(&tp->data.lock);

    /* Count of points */
    *point_num = (tp->data.points > max_point_num ? max_point_num : tp->data.points);
    for (size_t i = 0; i < *point_num; i++) {
        x[i] = tp->data.coords[i].x;
        y[i] = tp->data.coords[i].y;

        if (strength) {
            strength[i] = tp->data.coords[i].strength;
        }
    }
    /* Invalidate */
    tp->data.points = 0;
    portEXIT_CRITICAL(&tp->data.lock);

    return (*point_num > 0);
}

static esp_err_t del(esp_lcd_touch_handle_t tp)
{
    /* Reset GPIO pin settings */
    if (tp->config.int_gpio_num != GPIO_NUM_NC) {
        gpio_reset_pin(tp->config.int_gpio_num);
        if (tp->config.interrupt_callback) {
            gpio_isr_handler_remove(tp->config.int_gpio_num);
        }
    }
    if (tp->config.rst_gpio_num != GPIO_NUM_NC) {
        gpio_reset_pin(tp->config.rst_gpio_num);
    }
    /* Release memory */
    free(tp);

    return ESP_OK;
}

static esp_err_t reset(esp_lcd_touch_handle_t tp)
{
    if (tp->config.rst_gpio_num != GPIO_NUM_NC) {
        ESP_RETURN_ON_ERROR(gpio_set_level(tp->config.rst_gpio_num, tp->config.levels.reset), TAG, "GPIO set level failed");
        vTaskDelay(pdMS_TO_TICKS(200));
        ESP_RETURN_ON_ERROR(gpio_set_level(tp->config.rst_gpio_num, !tp->config.levels.reset), TAG, "GPIO set level failed");
        vTaskDelay(pdMS_TO_TICKS(200));
    }

    return ESP_OK;
}

static esp_err_t touch_init(esp_lcd_touch_handle_t tp)
{
    uint8_t data[3] = {0};

    ESP_RETURN_ON_ERROR(i2c_read_bytes(tp, 0x2000, data, 2), TAG, "I2C read failed");

    vTaskDelay(pdMS_TO_TICKS(1));
    data[0] = 0xc0;
    data[1] = 0x01;
    data[3] = 0x00;
    ESP_RETURN_ON_ERROR(i2c_write_bytes(tp, 0x00, data, 3), TAG, "I2C write failed");

    vTaskDelay(pdMS_TO_TICKS(1));
    data[0] = 0xc0;
    data[1] = 0x01;
    data[3] = 0x00;
    ESP_RETURN_ON_ERROR(i2c_write_bytes(tp, 0x01, data, 3), TAG, "I2C write failed");

    vTaskDelay(pdMS_TO_TICKS(1));
    data[0] = 0xc0;
    ESP_RETURN_ON_ERROR(i2c_write_bytes(tp, 0x04, data, 1), TAG, "I2C write failed");

    vTaskDelay(pdMS_TO_TICKS(1));
    data[0] = 0xc0;
    data[1] = 0x01;
    data[3] = 0x00;
    ESP_RETURN_ON_ERROR(i2c_write_bytes(tp, 0x02, data, 3), TAG, "I2C write failed");

    vTaskDelay(pdMS_TO_TICKS(1));
    data[0] = 0xc0;
    data[1] = 0x01;
    data[3] = 0x00;
    ESP_RETURN_ON_ERROR(i2c_write_bytes(tp, 0x03, data, 3), TAG, "I2C write failed");

    vTaskDelay(pdMS_TO_TICKS(1));
    data[0] = 0xc1;
    data[1] = 0x01;
    data[3] = 0x00;
    ESP_RETURN_ON_ERROR(i2c_write_bytes(tp, 0x04, data, 3), TAG, "I2C write failed");

    vTaskDelay(pdMS_TO_TICKS(1));
    data[0] = 0xc0;
    data[1] = 0x01;
    data[3] = 0x00;
    ESP_RETURN_ON_ERROR(i2c_write_bytes(tp, 0x01, data, 3), TAG, "I2C write failed");

    vTaskDelay(pdMS_TO_TICKS(1));
    data[0] = 0x00;
    ESP_RETURN_ON_ERROR(i2c_write_bytes(tp, 0x03, data, 1), TAG, "I2C write failed");

    vTaskDelay(pdMS_TO_TICKS(1));
    data[0] = 0x00;
    data[1] = 0x00;
    data[3] = 0x00;
    ESP_RETURN_ON_ERROR(i2c_write_bytes(tp, 0x19, data, 3), TAG, "I2C write failed");

    vTaskDelay(pdMS_TO_TICKS(1));
    data[0] = 0x00;
    data[1] = 0x00;
    data[3] = 0x00;
    ESP_RETURN_ON_ERROR(i2c_write_bytes(tp, 0x10, data, 3), TAG, "I2C write failed");

    vTaskDelay(pdMS_TO_TICKS(1));
    data[0] = 0x00;
    data[1] = 0x01;
    data[3] = 0x00;
    ESP_RETURN_ON_ERROR(i2c_write_bytes(tp, 0xc3, data, 3), TAG, "I2C write failed");

    vTaskDelay(pdMS_TO_TICKS(1));
    data[0] = 0x00;
    data[1] = 0x78;
    data[3] = 0x00;
    ESP_RETURN_ON_ERROR(i2c_write_bytes(tp, 0x20, data, 3), TAG, "I2C write failed");

    vTaskDelay(pdMS_TO_TICKS(1));
    data[0] = 0x00;
    data[1] = 0x01;
    data[3] = 0x00;
    ESP_RETURN_ON_ERROR(i2c_write_bytes(tp, 0x15, data, 3), TAG, "I2C write failed");

    vTaskDelay(pdMS_TO_TICKS(1));
    data[0] = 0x01;
    data[1] = 0xc6;
    data[3] = 0x00;
    ESP_RETURN_ON_ERROR(i2c_write_bytes(tp, 0xc0, data, 3), TAG, "I2C write failed");

    vTaskDelay(pdMS_TO_TICKS(1));
    data[0] = 0x01;
    data[1] = 0xc6;
    data[3] = 0x00;
    ESP_RETURN_ON_ERROR(i2c_write_bytes(tp, 0xc1, data, 3), TAG, "I2C write failed");

    vTaskDelay(pdMS_TO_TICKS(1));
    data[0] = 0x00;
    ESP_RETURN_ON_ERROR(i2c_write_bytes(tp, 0x06, data, 1), TAG, "I2C write failed");
    
    vTaskDelay(pdMS_TO_TICKS(1));
    data[0] = 0x00;
    ESP_RETURN_ON_ERROR(i2c_write_bytes(tp, 0x03, data, 1), TAG, "I2C write failed");

    vTaskDelay(pdMS_TO_TICKS(1));
    data[0] = 0x00;
    ESP_RETURN_ON_ERROR(i2c_write_bytes(tp, 0x03, data, 1), TAG, "I2C write failed");

    vTaskDelay(pdMS_TO_TICKS(1));
    data[0] = 0x00;
    ESP_RETURN_ON_ERROR(i2c_write_bytes(tp, 0x03, data, 1), TAG, "I2C write failed");

    vTaskDelay(pdMS_TO_TICKS(1));
    ESP_RETURN_ON_ERROR(i2c_read_bytes(tp, 0x2000, data, 2), TAG, "I2C read failed");

    vTaskDelay(pdMS_TO_TICKS(1));
    ESP_RETURN_ON_ERROR(i2c_read_bytes(tp, 0x1200, data, 2), TAG, "I2C read failed");

    vTaskDelay(pdMS_TO_TICKS(1));
    ESP_RETURN_ON_ERROR(i2c_read_bytes(tp, 0x2101, data, 2), TAG, "I2C read failed");

    vTaskDelay(pdMS_TO_TICKS(1));
    ESP_RETURN_ON_ERROR(i2c_read_bytes(tp, 0x1300, data, 2), TAG, "I2C read failed");

    vTaskDelay(pdMS_TO_TICKS(1));
    ESP_RETURN_ON_ERROR(i2c_read_bytes(tp, 0x1c00, data, 2), TAG, "I2C read failed");

    return ESP_OK;
}

static esp_err_t i2c_read_bytes(esp_lcd_touch_handle_t tp, uint16_t reg, uint8_t *data, uint8_t len)
{
    esp_err_t ret = ESP_OK;
    ESP_RETURN_ON_FALSE(data, ESP_ERR_INVALID_ARG, TAG, "Invalid data");
    uint8_t cmdlink_buffer[I2C_LINK_RECOMMENDED_SIZE(7)] = {0}; // only 7 operations will be queued in the handler ATTOW

    i2c_cmd_handle_t cmd_link = i2c_cmd_link_create_static(cmdlink_buffer, I2C_LINK_RECOMMENDED_SIZE(7));
    ESP_GOTO_ON_FALSE(cmd_link, ESP_ERR_NO_MEM, err, TAG, "no mem for i2c cmd link");
    ESP_GOTO_ON_ERROR(i2c_master_start(cmd_link), err, TAG, "issue start failed"); // start phase
    ESP_GOTO_ON_ERROR(i2c_master_write_byte(cmd_link, (ESP_LCD_TOUCH_IO_I2C_REALME_ADDRESS << 1) | I2C_MASTER_WRITE, true), err, TAG, "write address failed"); // address phase

    uint8_t cmds[4] = {BYTESHIFT(reg, 3), BYTESHIFT(reg, 2), BYTESHIFT(reg, 1), BYTESHIFT(reg, 0)};
    ESP_GOTO_ON_ERROR(i2c_master_write(cmd_link, cmds + (sizeof(cmds) - 2), 2, true), err, TAG, "write TOUCH cmd failed");

    if(data)
    {
        ESP_GOTO_ON_ERROR(i2c_master_start(cmd_link), err, TAG, "issue start failed"); // start phase
        ESP_GOTO_ON_ERROR(i2c_master_write_byte(cmd_link, (ESP_LCD_TOUCH_IO_I2C_REALME_ADDRESS << 1) | I2C_MASTER_READ, true), err, TAG, "write address failed"); // address phase
        ESP_GOTO_ON_ERROR(i2c_master_read(cmd_link, data, len, I2C_MASTER_LAST_NACK), err, TAG, "read data failed");
    }

    ESP_GOTO_ON_ERROR(i2c_master_stop(cmd_link), err, TAG, "issue stop failed"); // stop phase
    ESP_GOTO_ON_ERROR(i2c_master_cmd_begin(I2C_NUM_0, cmd_link, portMAX_DELAY), err, TAG, "i2c transaction failed");

    i2c_cmd_link_delete_static(cmd_link);
    
    return ESP_OK;
err:
    if (cmd_link) {
        i2c_cmd_link_delete_static(cmd_link);
    }
    return ret;   
}

static esp_err_t i2c_write_bytes(esp_lcd_touch_handle_t tp, uint16_t reg, uint8_t *data, uint8_t len)
{
    ESP_RETURN_ON_FALSE(data, ESP_ERR_INVALID_ARG, TAG, "Invalid data");

    return esp_lcd_panel_io_tx_param(tp->io, reg, data, len);
}
